package com.ruoyi.common.service.impl;

import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.entity.CardCode;
import com.ruoyi.common.entity.dto.CardCodeDto;
import com.ruoyi.common.mapper.CardCodeMapper;
import com.ruoyi.common.service.ICardCodeService;
import com.ruoyi.common.utils.DateUtils;

import com.ruoyi.common.utils.SecurityUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.security.SecureRandom;
import java.security.Security;
import java.time.Duration;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.List;

/**
 * 卡密Service业务层处理
 * 
 * @author ruoyi
 * @date 2025-08-17
 */
@Service
@Slf4j
public class CardCodeServiceImpl implements ICardCodeService
{
    @Autowired
    private CardCodeMapper cardCodeMapper;

    // 使用安全随机数生成器
    private static final SecureRandom secureRandom = new SecureRandom();

    private static final String CHARACTERS = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789";
    // 去除易混淆字符
    private static final int BASE_LENGTH = 23; // 基础长度
    @Autowired
    private  StringRedisTemplate redisTemplate;

    /**
     * 查询卡密
     * 
     * @param id 卡密主键
     * @return 卡密
     */
    @Override
    public CardCode selectCardCodeById(Long id)
    {
        return cardCodeMapper.selectCardCodeById(id);
    }

    /**
     * 查询卡密列表
     * 
     * @param cardCode 卡密
     * @return 卡密
     */
    @Override
    public List<CardCode> selectCardCodeList(CardCode cardCode)
    {
        return cardCodeMapper.selectCardCodeList(cardCode);
    }

    /**
     * 新增卡密
     * 
     * @param cardCode 卡密
     * @return 结果
     */
    @Override
    @Transactional
    public int insertCardCode(CardCode cardCode)
    {
        Date nowDate = DateUtils.getNowDate();
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(nowDate);
        calendar.add(Calendar.DAY_OF_MONTH, 1);
        Date tomorrow  = calendar.getTime();

        cardCode.setCreateTime(nowDate);
        cardCode.setExpireTime(tomorrow);
        int i = cardCodeMapper.insertCardCode(cardCode);
        redisTemplate.opsForValue().set(cardCode.getCardCode(), cardCode.getCardCode(), Duration.ofHours(24L));
        return i;

    }

    /**
     * 修改卡密
     * 
     * @param cardCode 卡密
     * @return 结果
     */
    @Override
    public int updateCardCode(CardCode cardCode)
    {
        cardCode.setUpdateTime(DateUtils.getNowDate());
        return cardCodeMapper.updateCardCode(cardCode);
    }

    /**
     * 批量删除卡密
     * 
     * @param ids 需要删除的卡密主键
     * @return 结果
     */
    @Override
    public int deleteCardCodeByIds(Long[] ids)
    {
        CardCode cardCode =null;
        for (Long id : ids) {
            cardCode = cardCodeMapper.selectCardCodeById(id);
            if (cardCode != null) {
                redisTemplate.delete(cardCode.getCardCode());
            }
        }

        return cardCodeMapper.deleteCardCodeByIds(ids);
    }

    /**
     * 删除卡密信息
     * 
     * @param id 卡密主键
     * @return 结果
     */
    @Override
    public int deleteCardCodeById(Long id)
    {
        return cardCodeMapper.deleteCardCodeById(id);
    }




    @Override
    public AjaxResult getCode() {

        StringBuilder sb = new StringBuilder(BASE_LENGTH);

        // 生成23位随机字符
        for (int i = 0; i < BASE_LENGTH; i++) {
            sb.append(CHARACTERS.charAt(secureRandom.nextInt(CHARACTERS.length())));
        }

        // 计算校验位
        char checkDigit = calculateCheckDigit(sb.toString());
        sb.append(checkDigit);
        String code = sb.toString();
        // 卡密存活时间为24小时


        return AjaxResult.success(code);
    }
    @Override
    public R checkcode(CardCodeDto cardCode) {
        Long userId = SecurityUtils.getUserId();
        String cardCodeStr = cardCode.getCardCode();
        CardCode oldcardCode = cardCodeMapper.selectOne(Wrappers.<CardCode>lambdaQuery().eq(CardCode::getCardCode, cardCodeStr));
        // 1. 二次校验参数（防止DTO校验被绕过）

        if (cardCodeStr == null || cardCodeStr.trim().isEmpty()) {
            log.warn("卡密校验失败：卡密为空");
            return R.fail("卡密不能为空");
        }



        try {
            // 4. 使用Redis Lua脚本实现原子操作（查询+删除）
            // 脚本逻辑：存在则删除并返回1，不存在返回0
            String luaScript = "local val = redis.call('get', KEYS[1]); " +
                    "if val then redis.call('del', KEYS[1]); return 1; " +
                    "else return 0; end";

            // 创建RedisScript对象，指定返回类型为Long
            DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>(luaScript, Long.class);


            Long result = redisTemplate.execute(redisScript,
                    Collections.singletonList(cardCodeStr));


            // 5. 处理校验结果
            if (result != null && result == 1) {

                oldcardCode.setCardCode(cardCodeStr);
                oldcardCode.setUserId(userId);
                oldcardCode.setStatus(1L);
                oldcardCode.setUpdateTime(DateUtils.getNowDate());
                oldcardCode.setUsageTime(DateUtils.getNowDate());
                cardCodeMapper.updateCardCode(oldcardCode);
                return R.ok("验证成功");
            } else {
                return R.fail("验证失败,请查看您的卡密是否正确");
            }

        } catch (Exception e) {
            // 6. 捕获Redis异常（如连接超时、宕机等）
            log.error("异常信息：{}", e.getMessage(), e);
            return R.fail("系统繁忙，请稍后再试");
        }
    }

    /**
     * 计算校验位（简单示例，可替换为更复杂的算法）
     */
    private char calculateCheckDigit(String baseCode) {
        int sum = 0;
        for (int i = 0; i < baseCode.length(); i++) {
            sum += baseCode.charAt(i) * (i + 1);
        }
        return CHARACTERS.charAt(sum % CHARACTERS.length());
    }
}
